LambdaオーソライザーでLINEミニアプリ用APIのアクセス制御をする #LINE_API
はじめに
こんにちは、中村です。
LIFFアプリおよびサーバーでユーザー情報を使用するにてLINE公式アナウンスされておりますが、LINEミニアプリ(LIFFアプリ)においてはフロントで取得したIDトークンやアクセストークンを元にAPIを操作することが想定されます。今回はAPI GatewayのLambdaオーソライザーを使ってAPIへのアクセス制御を実現します。
LINEミニアプリ用にLambdaオーソライザーでアクセス制御する
今回は下記のようにAPI GatewayとLambdaを使ってサーバレスAPIにします。
API検証にあたりIDトークンを取得するLINEミニアプリ(LIFFアプリ)が必要です。LIFFに登録するためには、HTTPS
のエンドポイントを用意してください。またIDトークンを利用するためScopeでopenid
を選択します。LIFF SDKの読み込み・下記のコードの実行することでLIFFアプリ内でIDトークンを取得することができます。
取得するためのコード
liff.init({ liffId: 'xxxxxxxxxx-xxxxxxx' }).then(() => { if (!liff.isLoggedIn()) { liff.login(); // External browser support } const idToken = liff.getIDToken(); // console.log() or display id token. }).catch((err) => { alert(err.code, err.message); })
Lambda
構成図を見ていただくと2つのLambda関数がありますが、今回は認証の部分の話をしたいのでLambdaAuth
のコードについて説明します。LambdaBackend
は、LambdaのHelloWorld関数を利用します。Lambdaのメイン処理でAuthorizationヘッダーのIDトークンを取得しIDトークンを検証するAPIを実行します。その結果に応じてポリシーAllow
・Deny
を分岐します。
Lambda環境変数 - CLIENT_IDは、LINEログインのチャネルIDを入力してください。
const fetch = require('node-fetch'); exports.handler = async (event) => { console.log('Event: ' + JSON.stringify(event, null, 2)); /** * Get id token from `Bearer xxxxxxxxxxxxxxxxxx` */ const [, idToken] = event.headers['Authorization'].split(' '); /** * Verify id token using social API v2.1 and create policy */ return await fetch('https://api.line.me/oauth2/v2.1/verify', { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: `id_token=${idToken}&client_id=${process.env.CLIENT_ID}` }) .then((res) => res.json()) .then((json) => { console.log('Social API Response: ' + JSON.stringify(json, null, 2)); if (json.error) { return generatePolicy('user', 'Deny', event.methodArn); } else { return generatePolicy('user', 'Allow', event.methodArn, json.sub); } }); }; const generatePolicy = (principalId, effect, resource, userId) => { let authReponse = {}; authReponse.principalId = principalId; if (effect && resource) { let policyDocument = {}; policyDocument.Version = '2012-10-17'; policyDocument.Statement = []; let statementOne = {}; statementOne.Action = 'execute-api:Invoke'; statementOne.Effect = effect; statementOne.Resource = resource; policyDocument.Statement.push(statementOne); authReponse.policyDocument = policyDocument; } if (userId) authReponse.context = { userId: userId }; console.log('Policy: ' + JSON.stringify(authReponse, null, 2)); return authReponse; };
Lambda2つを作成したら、API Gatewayの設定に移ります。
API Gateway
API Gatewayにアクセスして、APIの作成をクリックします。REST API
を構築しましょう。下記の設定を入力しAPIの作成
をクリックします。
項目名 | 値 |
---|---|
プロトコルを選択する | REST |
新しいAPIの作成 | 新しいAPI |
API名 | 任意 |
API作成が成功したらサイドバーのオーソライザー
にアクセスし、新しいオーソライザーの作成
をクリックします。下記の設定でオーソライザーを作成してください。
項目名 | 値 |
---|---|
名前 | 任意 |
タイプ | Lambda |
Lambda関数 | LambdaAuth 用の関数 |
Lambdaイベントペイロード | リクエスト |
IDソース | ヘッダー・Authorization |
認可のキャッシュ | 無効 |
作成が完了すると、登録内容としては以下のようになると思います。テストをクリックして実際に正常に動作するか確認しましょう。
AuthorizationヘッダーがBearer ${id_token}
でリクエストされる想定ですので、下記のように指定してください。
検証成功・失敗は下記の画像を参考にしてください。成功の場合はLambdaBackend
にリクエストがされます。
検証失敗されると、LambdaBackend
へはアクセスされず403が返されます。
ここまで確認ができたらあとはLambdaBackend
をメソッドに登録します。サイドバーからリソースへアクセスしてください。今回はルートにPOST
でリクエストするメソッドを作成します。アクション
プルダウンからメソッドの作成
をクリックします。リソースパスの下にメソッドのプルダウンが表示されますので、POST
を選択しチェックアイコンをクリックします。
作成が完了すると、統合ポイント(バックエンド)の登録に入ります。下記の設定を入力して保存
をクリックします。保存しようとするとAPI GatewayからLambdaを実行する権限を付与する旨のメッセージが表示されるのでOKで権限を付与します。
項目名 | 値 |
---|---|
統合タイプ | Lambda関数 |
Lambdaプロキシ統合の使用 | チェックする |
Lambdaリージョン | LambdaBackend のリージョン |
Lambda関数 | LambdaBackend を選択 |
ここまで完了すると、下記の画面になりますのでオーソライザーの設定をします。メソッドリクエスト
をクリックしてください。
認可
の編集アイコンをタップして先ほど作成したオーソライザーを選択し保存します。
さて最後にAPIをデプロイして完成です。アクション
プルダウンからAPIのデプロイ
をクリックします。デプロイするステージ名を入力してデプロイ
をクリックします。デプロイが完了すると、ステージへ遷移しAPIのエンドポイントが確認できます。こちらをメモしておいてください。
テスト
デプロイが完了したので、Postmanを使ってAPIにアクセスしてみます。最初に用意したLIFFアプリからIDトークンを取得して実行してみます。
IDトークンが検証できた場合はバックエンドまでリクエストされ200が返され、失敗時は403が返されました。IDトークンによりAPIアクセス制御ができることが確認できました。
まとめ
今回は、LINEミニアプリ(LIFFアプリ)で利用するAPIの認証をAPI GatewayのLambdaオーソライザーを使って実現しました。現時点でHTTP APIのJWTオーソライザーではまだES256アルゴリズムが未対応のため
利用できませんでしたが、対応されればこのソースを書くこともなくなるでしょう。LINEミニアプリ(LIFFアプリ)はサーバレスで!